package websocket

import (
	"encoding/json"
	"fmt"
	"gin_chat_demo/config/zap"
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"time"
)

type Server struct {
	routes map[string]HandlerFunc
	addr   string
	patten string
	sync.RWMutex
	authentication Authentication
	connToUser     map[*Conn]string
	userToConn     map[string]*Conn
	upgrader       websocket.Upgrader
	opt            *serverOption
}

func NewServer(addr string, opts ...ServerOptions) *Server {
	opt := newServerOptions(opts...)
	return &Server{
		routes:         make(map[string]HandlerFunc),
		addr:           addr,
		upgrader:       websocket.Upgrader{},
		opt:            &opt,
		authentication: opt.Authentication,
		patten:         opt.patten,
		connToUser:     make(map[*Conn]string),
		userToConn:     make(map[string]*Conn),
	}
}

func (s *Server) ServerWs(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if r := recover(); r != nil {
			zap.Logger.Errorf("server handler ws recover err %v", r)
		}
	}()
	conn := NewConn(s, w, r)
	if conn == nil {
		return
	}

	//进行鉴权
	if !s.authentication.Auth(w, r) {
		s.Send(&Message{FrameType: FrameData, Data: fmt.Sprint("不具备访问权限")}, conn)
		conn.Close()
		return
	}

	//记录连接
	s.addConn(conn, r)

	go s.handlerConn(conn)

}

func (s *Server) addConn(conn *Conn, req *http.Request) {
	uid := s.authentication.UserId(req)
	s.RWMutex.Lock()
	defer s.RWMutex.Unlock()

	//验证用户是否之前登入过
	if c := s.userToConn[uid]; c != nil {
		//将之前的连接给踢掉
		c.Close()
	}
	s.connToUser[conn] = uid
	s.userToConn[uid] = conn
}

// 根据连接对象执行任务处理
func (s *Server) handlerConn(conn *Conn) {
	//设置心跳间隔时间
	heartbeatInterval := 10 * time.Second

	// 设置一个最后接收到 ping 消息的时间戳
	lastPingTime := time.Now()

	// 进行设置ping 消息的状态
	pingReceived := true

	// 启动一个协程定期检查连接状态
	go func() {
		for {
			select {
			case <-time.After(heartbeatInterval):
				if pingReceived && time.Now().Sub(lastPingTime) > heartbeatInterval {
					s.Close(conn)
					return
				}
				pingReceived = true
			}
		}
	}()

	for {
		// 获取请求的消息
		_, msg, err := conn.ReadMessage()
		if err != nil {
			zap.Logger.Errorf("websocket conn read message err %v", err)
			s.Close(conn)
			return
		}
		//进行解析消息
		var message Message
		if err = json.Unmarshal(msg, &message); err != nil {
			zap.Logger.Errorf("json unmarshal err %v, msg %v", err, string(msg))
			s.Close(conn)
			return
		}
		//依据消息进行处理
		switch message.FrameType {
		case FramePing:
			// 更新最后接收到 ping 的时间戳
			lastPingTime = time.Now()
			pingReceived = false
			s.Send(&Message{FrameType: FramePing}, conn)
		case FrameData:
			// 根据请求的method 分发路由并执行
			if handler, ok := s.routes[message.Method]; ok {
				handler(s, conn, &message)
			} else {
				s.Send(&Message{FrameType: FrameData, Data: fmt.Sprintf("不存在执行的方法 %v 请检查", message.Method)}, conn)
			}
		}

		if pingReceived && time.Now().Sub(lastPingTime) > heartbeatInterval {
			s.Close(conn)
			return
		}

	}
}

func (s *Server) GetConn(uid string) *Conn {
	s.RWMutex.RLock()
	defer s.RWMutex.RUnlock()
	return s.userToConn[uid]
}

func (s *Server) GetConns(uids ...string) []*Conn {
	if len(uids) == 0 {
		return nil
	}
	s.RWMutex.RLock()
	defer s.RWMutex.RUnlock()

	res := make([]*Conn, 0, len(uids))
	for _, uid := range uids {
		res = append(res, s.userToConn[uid])
	}
	return res
}

func (s *Server) GetUsers(conns ...*Conn) []string {
	s.RWMutex.RLock()
	defer s.RWMutex.RUnlock()

	var res []string
	if len(conns) == 0 {
		//获取全部
		res = make([]string, 0, len(s.connToUser))
		for _, uid := range s.connToUser {
			res = append(res, uid)
		}
	} else {
		//获取部分
		res = make([]string, 0, len(conns))
		for _, conn := range conns {
			res = append(res, s.connToUser[conn])
		}
	}
	return res
}

func (s *Server) Close(conn *Conn) {
	s.RWMutex.Lock()
	defer s.RWMutex.Unlock()
	uid := s.connToUser[conn]
	if uid == "" {
		//已经被关闭了
		return
	}
	delete(s.connToUser, conn)
	delete(s.userToConn, uid)
	conn.Close()
}

func (s *Server) SendByUserId(msg interface{}, sendIds ...string) error {
	if len(sendIds) == 0 {
		return nil
	}

	return s.Send(msg, s.GetConns(sendIds...)...)
}

func (s *Server) Send(msg interface{}, conns ...*Conn) error {
	if len(conns) == 0 {
		return nil
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	for _, conn := range conns {
		if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
			return err
		}
	}
	return nil
}

func (s *Server) AddRouters(rs []Route) {
	for _, r := range rs {
		s.routes[r.Method] = r.Handler
	}
}

func (s *Server) Start() {
	http.HandleFunc(s.patten, s.ServerWs)
	zap.Logger.Info(http.ListenAndServe(s.addr, nil))
}

func (s *Server) Stop() {
	fmt.Println("停止websocket")
}
